home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / Snippets / Printing / Rotated Thingies / …using Text PicComments / Rotn Text.p < prev    next >
Encoding:
Text File  |  1992-07-15  |  11.8 KB  |  488 lines  |  [TEXT/MPS ]

  1. {**
  2.  **     Program: Rotn Text
  3.  **        7/25/91
  4.  **
  5.  **        MPW 3.2 Pascal source.
  6.  **
  7.  **     Purpose:
  8.  **
  9.  **        Rotn Text demonstrates how to rotate text on PostScript printers
  10.  **        using the TextBegin/End PicComments.  PrGeneral is used to change
  11.  **        the resolution, which throws another wrench in the works; you have
  12.  **        to scale everything.
  13.  **        
  14.  **        Dave Hersey
  15.  **
  16.  **        Macintosh Developer Technical Support
  17.  **
  18.  **        ----
  19.  **             ver. 1.0    7/25/91        dmh
  20.  **
  21.  **}
  22.  
  23. PROGRAM RotnText;
  24.  
  25. USES
  26.     MemTypes, QuickDraw, OSIntf, ToolIntf, Traps, MacPrint, Packages;
  27.  
  28. CONST
  29.  
  30.     {The following constants are used to identify menus and their items. The menu IDs
  31.      have an "m" prefix and the item numbers within each menu have an "i" prefix.}
  32.  
  33.     rMenuBar    = 128;                    {menubar}
  34.  
  35.     mApple        = 128;                    {Apple menu}
  36.     iAbout        = 1;
  37.  
  38.     mFile        = 129;                    {File menu}
  39.     iPrint        = 1;
  40.     iQuit        = 3;
  41.  
  42.     mEdit        = 130;                    {Edit menu}
  43.  
  44.  
  45. VAR
  46.  
  47.     gQuitting    : Boolean;                {"Are we all done?" flag}
  48.  
  49.  
  50. {*------ RotateText -----------------------------------------------------------------*}
  51.  
  52. {**
  53.  **      RotateText draws the passed text rotated 45° using the TextBegin/End
  54.  **      comments.  The center of rotation and start of text drawing is done
  55.  **      at the "where" point passed.
  56.  **}
  57.  
  58. {$S Main}
  59. PROCEDURE RotateText(theText : Str255; where : Point);
  60.  
  61.     CONST
  62.         TextBegin    = 150;
  63.         TextEnd        = 151;
  64.         TextCenter    = 154;
  65.  
  66.     TYPE
  67.         TTxtCenterHdl    = ^TTxtCenterPtr;
  68.         TTxtCenterPtr    = ^TTxtCenterRec;
  69.         TTxtCenterRec    = RECORD
  70.                             y, x: Fixed;    {offset from current pen location to center of rotation.}
  71.                           END;     { TTxtCenterRec }
  72.  
  73.         TTxtPicHdl        = ^TTxtPicPtr;
  74.         TTxtPicPtr        = ^TTxtPicRec;
  75.         TTxtPicRec        = PACKED RECORD
  76.                             tJus: Byte;        {0,1,2,3,4 -> none, left, center, right, full justification.}
  77.                             tFlip: Byte;    {0,1,2 -> none, horizontal, vertical coordinate flip.}
  78.                             tRot: integer;    {0..360 -> clockwise rotation in degrees.}
  79.                             tLine: Byte;    {1,2,3.. -> single, 1 1/2, double.. spacing}
  80.                             tCmnt: Byte;    {Reserved.}
  81.                           END;     { TTxtPicRec }
  82.  
  83.     VAR
  84.         txtHdl        : TTxtPicHdl;
  85.         txtCtr        : TTxtCenterHdl;
  86.  
  87.     BEGIN
  88.  
  89.     {Do a dummy draw to set the clipping region.}
  90.  
  91.         PenSize(0, 0);
  92.         MoveTo(0, 0);
  93.         LineTo(0, 0);
  94.         PenSize(1, 1);
  95.  
  96.         txtCtr := TTxtCenterHdl(NewHandle(SizeOf(TTxtCenterRec)));
  97.         txtCtr^^.y := 0; {no y offset to center of rotation}
  98.         txtCtr^^.x := 0; {no x offset to center of rotation}
  99.  
  100.         txtHdl := TTxtPicHdl(NewHandle(SizeOf(TTxtPicRec)));
  101.         txtHdl^^.tJus := 0;     {no justification}
  102.         txtHdl^^.tFlip := 0; {no flip}
  103.         txtHdl^^.tRot := 45; {45° rotation}
  104.         txtHdl^^.tLine := 1; {single spacing}
  105.  
  106.     {Begin text rotation.}
  107.  
  108.         PicComment(TextBegin,GetHandleSize(handle(txtHdl)),handle(txtHdl));
  109.  
  110.     {Set the center of rotation and draw.}
  111.  
  112.         MoveTo(where.h, where.v);
  113.         PicComment(TextCenter,GetHandleSize(handle(txtCtr)),handle(txtCtr));
  114.  
  115.         DrawString(theText);
  116.  
  117.     {Text rotation ends here.}
  118.  
  119.         PicComment(TextEnd,0,NIL);
  120.         DisposHandle(handle(txtHdl)); {Clean up}
  121.         DisposHandle(handle(txtCtr));
  122.  
  123.     END;    {**  RotateText  **}
  124.  
  125.  
  126. {*------ DrawStuff -----------------------------------------------------------------*}
  127.  
  128. {**
  129.  **      DrawStuff draws the objects.  prRsl is the resolution of the printer's
  130.  **        GrafPort and is used to determine the amount to scale everything.
  131.  **}
  132.  
  133. {$S Main}
  134.  PROCEDURE DrawStuff(theGPort : GrafPtr; prRsl : Integer);
  135.  
  136.  VAR
  137.     oldPort        : GrafPtr;
  138.     fNum        : Integer;
  139.     scalar        : Real;
  140.     where        : Point;
  141.  
  142.  BEGIN
  143.  
  144. {Get the current port and save it.}
  145.     
  146.     GetPort(oldPort);
  147.     SetPort(theGPort);
  148.  
  149.     scalar := prRsl;
  150.     scalar := scalar / 72;
  151.  
  152.     GetFNum('Times', fNum);
  153.     TextFont(fNum);
  154.     TextSize(Round(24 * scalar));
  155.  
  156.     where.h := Round(50 * scalar);
  157.     where.v := Round(50 * scalar);
  158.     RotateText('This is a line of rotated text.', where);
  159.  
  160.     SetPort(oldPort);
  161.  
  162.  END;    {**  DrawStuff  **}
  163.  
  164.  
  165. {*------ GetBestRsl -----------------------------------------------------------------*}
  166.  
  167. {**
  168.  **      GetBestRsl determines the best "square" resolution supported by the printer.
  169.  **        For example, 300 dpi horizontal by 300 dpi vertical.  It isn't necessary to
  170.  **        use square resolutions, but it generally proves easier.  We use PrGeneral and
  171.  **        the getRslDataOp opCode to get a list of the supported resolutions for our
  172.  **        printer.  Then we just go through the rgRslRec and find the maximum square
  173.  **        resolution for discrete or non-discrete data, whichever we have.  Finally, we
  174.  **        make sure it's divisible by 72 for cleaner scaling.
  175.  **}
  176.  
  177. {$S Main}
  178. FUNCTION GetBestRsl :Integer;
  179.  
  180. VAR
  181.         err            : OSErr;
  182.         theRes, num    : Integer;
  183.         getRslData    : TGetRslBlk;
  184.  
  185. BEGIN
  186.  
  187. {Start off with our maximum resolution at 0, then call PrGeneral and parse our list
  188.  of returned values.}
  189.  
  190.         theRes := 0;
  191.         getRslData.iOpCode := getRslDataOp;
  192.  
  193.         PrGeneral(@getRslData);
  194.         err := getRslData.iError;
  195.  
  196. {If our printer only supports discrete resolutions, find the largest square one and
  197.  use that.  If our printer supports a range of resolutions, choose the smaller of the
  198.  maximum X and Y resolutions, then make it divisible by 72 for cleaner scaling.}
  199.  
  200.         IF (err = noErr) THEN
  201.             IF (getRslData.XRslRg.iMax = 0) AND (getRslData.YRslRg.iMax = 0) THEN
  202.                 BEGIN                                                                {Discrete resolutions.}
  203.                     FOR num := 1 TO getRslData.iRslRecCnt DO
  204.                         IF (getRslData.rgRslRec[num].iXRsl = getRslData.rgRslRec[num].iYRsl)
  205.                             AND (theRes < getRslData.rgRslRec[num].iXRsl) THEN
  206.                                 theRes := getRslData.rgRslRec[num].iXRsl;
  207.                 END
  208.             ELSE
  209.                 BEGIN                                                                {Variable resolutions.}
  210.                     IF (getRslData.XRslRg.iMax < getRslData.YRslRg.iMax) THEN
  211.                         theRes := (getRslData.XRslRg.iMax DIV 72) * 72            {Use multiple of 72 closest to max. X resolution.}
  212.                     ELSE
  213.                         theRes := (getRslData.YRslRg.iMax DIV 72) * 72            {Use multiple of 72 closest to max. Y resolution.}
  214.                 END;
  215.             
  216.  
  217. {In the unlikely event that PrGeneral fails and theRes is still 0, set it to 72.
  218.  This most likely is a supported resolution.  Finally return the best resolution we
  219.  could find.}
  220.  
  221.         IF theRes = 0 THEN theRes := 72;
  222.         GetBestRsl := theRes;
  223.  
  224. END;    {**  GetBestRsl  **}
  225.  
  226.  
  227. {*------ PrintStuff ----------------------------------------------------------------*}
  228. {**
  229.  **        PrintStuff will call all of the necessary Print Manager calls to print 
  230.  **        a document. It checks PrError after each Print Manager call. If an error 
  231.  **     is found, all of the Print Manager open calls (i.e. PrOpen, PrOpenDoc...) 
  232.  **        will have a corresponding close call before the error is posted to the user. 
  233.  **        You want to use this approach to make sure the Print Manager closes properly 
  234.  **        and all temporary memory is released.
  235.  **}
  236.  
  237. {$S Main}
  238. PROCEDURE PrintStuff;
  239.  
  240. VAR
  241.     Loop,
  242.     NumberOfPages,
  243.     PageNumber        : Integer;
  244.     PrintError        : LongInt;
  245.     oldPort          : GrafPtr;
  246.     thePrRecHdl        : THPrint;
  247.     thePrPort        : TPPrPort;
  248.     theStatus        : TPrStatus;
  249.     errStr            : Str255;
  250.     rslData            : TSetRslBlk;
  251.     err                : OSErr;
  252.     prRsl            : Integer;
  253.     setMHdl            : MenuHandle;
  254.     mark            : Char;
  255.     bestRsl            : Integer;
  256.  
  257. BEGIN
  258.  
  259.  
  260. {Get our current port and create a print handle.  If no errors,
  261.  do our PrOpen call and, if no errors again, get the default
  262.  settings for the current driver.}
  263.  
  264.     GetPort(oldPort);
  265.     thePrRecHdl := THPrint(NewHandle(sizeof(TPrint)));
  266.     
  267.     IF (MemError = noErr) THEN
  268.     BEGIN
  269.         PrOpen;
  270.         IF (PrError = noErr) THEN
  271.         BEGIN
  272.             PrintDefault(thePrRecHdl);
  273.  
  274.             bestRsl := GetBestRsl;
  275.             rslData.iOpCode := SetRslOp;
  276.             rslData.hPrint := thePrRecHdl;
  277.             rslData.iXRsl := bestRsl;
  278.             rslData.iYRsl := bestRsl;
  279.             PrGeneral(@rslData);
  280.             err := rslData.iError;
  281.     
  282.             prRsl := bestRsl;
  283.  
  284.  
  285. {If we still have no errors, give style and print job dialogs, then open a
  286.  document and its page.  Keep checking for those dang printer errors.}
  287.  
  288.             IF (PrError = noErr) THEN
  289.             BEGIN
  290.                 IF (PrStlDialog(thePrRecHdl)) THEN
  291.                 BEGIN
  292.                     IF (PrJobDialog(thePrRecHdl)) THEN 
  293.                     BEGIN
  294.                         thePrPort := PrOpenDoc(thePrRecHdl, NIL, NIL);
  295.                                
  296.                         IF (PrError = noErr) THEN
  297.                         BEGIN
  298.                             PrOpenPage(thePrPort, NIL);
  299.  
  300.  
  301. {If we're still running error-free, draw our test page.  prRsl is the
  302.  resolution of our printer port.}
  303.  
  304.                             IF (PrError = noErr) THEN
  305.                                 DrawStuff(GrafPtr(thePrPort), prRsl);
  306.  
  307.  
  308. {When done, close our page and document and spool the document if necessary.  When
  309.  finshed, call PrClose to end the whole shabang.}
  310.  
  311.                             PrClosePage(thePrPort);
  312.                         END;
  313.                              
  314.                         PrCloseDoc(thePrPort);
  315.                              
  316.                         IF (thePrRecHdl^^.prJob.bJDocLoop = bSpoolLoop) and (PrError = noErr) THEN
  317.                             PrPicFile(thePrRecHdl, NIL, NIL, NIL, theStatus);
  318.                     END;
  319.                 END;
  320.             END;
  321.         END;
  322.         
  323.         PrClose;
  324.  
  325.     END;
  326.  
  327. END;    {**  PrintStuff  **}
  328.  
  329.  
  330. {*------ Initialize ----------------------------------------------------------------*}
  331. {**
  332.  **        Initialize just handles necessary Toolbox initializing, setting our quitting 
  333.  **        flag to FALSE and installing our menus.
  334.  **}
  335.  
  336. {$S Initialize}
  337. PROCEDURE Initialize;
  338.  
  339. VAR
  340.     menuBar    : Handle;
  341.     
  342. BEGIN
  343.  
  344.     InitGraf(@thePort);
  345.     InitFonts;
  346.     InitWindows;
  347.     InitMenus;
  348.     TEInit;
  349.     InitDialogs(NIL);
  350.     InitCursor;
  351.     FlushEvents(everyEvent, 0);    
  352.  
  353.     gQuitting := FALSE;
  354.  
  355.     menuBar := GetNewMBar(rMenuBar);        {read menus into menu bar}
  356.     IF (menuBar = NIL) THEN ExitToShell;    {should do real error stuff here.}
  357.     SetMenuBar(menuBar);                    {install menus}
  358.     DisposHandle(menuBar);
  359.     AddResMenu(GetMHandle(mApple), 'DRVR');    {add DA names to Apple menu}
  360.     DrawMenuBar;
  361.  
  362. END;    {**  Initialize  **}
  363.  
  364.  
  365. {$S _DataInit}
  366. PROCEDURE _DataInit; EXTERNAL;
  367.  
  368. {This routine is automatically linked in by the MPW Linker. This external
  369.  reference to it is done so that we can unload its segment, %A5Init.}
  370.  
  371.  
  372.  
  373. {*------ DoMenuCommand ----------------------------------------------------------------*}
  374. {**
  375.  **        DoMenuCommand is called when an item is chosen from the menu bar (after calling 
  376.  **        MenuSelect or MenuKey).  It does the right thing for each command.
  377.  **}
  378.  
  379. {$S Main}
  380. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  381.  
  382. VAR
  383.     menuID, menuItem    : INTEGER;
  384.     daRefNum            : INTEGER;
  385.     daName                : Str255;
  386.  
  387. BEGIN
  388.  
  389. {Get the menu ID and item ID.}
  390.  
  391.     menuID := HiWrd(menuResult);
  392.     menuItem := LoWrd(menuResult);
  393.  
  394.     CASE menuID OF
  395.         mApple:
  396.             CASE menuItem OF
  397.                 iAbout:                {bring up alert for About}
  398.                     (* We do nothing here... *);
  399.  
  400.                 OTHERWISE
  401.                 BEGIN        {all non-About items in this menu are DAs}
  402.                     GetItem(GetMHandle(mApple), menuItem, daName);
  403.                     daRefNum := OpenDeskAcc(daName);
  404.                 END;
  405.             END;
  406.  
  407.         mFile:                            {File Menu}
  408.             CASE menuItem OF
  409.                 iPrint:                        {-> Print Test Page.}
  410.                     PrintStuff;
  411.                 iQuit:
  412.                     gQuitting := TRUE;        {-> Quit}
  413.             END;
  414.     END;        
  415.  
  416.     HiliteMenu(0);
  417.  
  418. END;    {**  DoMenuCommand  **}
  419.  
  420.  
  421. {*------ DoEvent ----------------------------------------------------------------*}
  422. {**
  423.  **        DoEvent handles incoming events for our app.  In this skimpy sample, we
  424.  **        only handle menu events and system clicks.
  425.  **}
  426.  
  427. {$S Main}
  428. PROCEDURE DoEvent;
  429.  
  430. VAR
  431.     part        : INTEGER;
  432.     key            : Char;
  433.     quitting    : Boolean;
  434.     event        : EventRecord;
  435.     window        : WindowPtr;
  436.  
  437. BEGIN
  438.  
  439. {Repeatedly handle menu selecting events until our quit flag is set.}
  440.  
  441.     REPEAT
  442.         BEGIN
  443.             SystemTask;                                    {This must be called if using GetNextEvent}
  444.  
  445.             IF (GetNextEvent(everyEvent, event)) THEN
  446.                 CASE event.what OF
  447.                     mouseDown:
  448.                         BEGIN
  449.                             part := FindWindow(event.where, window);
  450.                             CASE part OF
  451.                                 inMenuBar:
  452.                                     DoMenuCommand(MenuSelect(event.where));
  453.                 
  454.                                 inSysWindow:
  455.                                     SystemClick(event, window);
  456.                             END;
  457.                         END;
  458.     
  459.                     keyDown, autoKey:
  460.                         BEGIN
  461.                             key := CHR(BAnd(event.message, charCodeMask));
  462.                             IF (BAnd(event.modifiers, cmdKey) <> 0) AND (event.what = keyDown) THEN
  463.                                 DoMenuCommand(MenuKey(key));
  464.                         END;
  465.                 END;
  466.         END;
  467.     UNTIL gQuitting;
  468.  
  469. END;    {**  DoEvent  **}
  470.  
  471.  
  472. {*------ Main ----------------------------------------------------------------*}
  473. {**
  474.  **        Main kickstarts our app.
  475.  **}
  476.  
  477. {$S Main}
  478. BEGIN
  479.  
  480.     UnloadSeg(@_DataInit);    {note that _DataInit must not be in Main!}
  481.     MaxApplZone;            {expand the heap so code segments load at the top}
  482.     Initialize;                {initialize the program}
  483.     UnloadSeg(@Initialize);    {note that Initialize must not be in Main!}
  484.     DoEvent;                {handle menu events until quitting.}
  485.  
  486. END.    {**  Rotn Text.  **}
  487.  
  488.